﻿using UnityEngine;
using UnityEngine.Extension;
using UnityEngine.UI;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.Extension;
using UnityEditor.UI;
using System;
using System.Extension;
using System.Reflection.Extension;
using System.Reflection;
[CustomPropertyDrawer(typeof(CommandSubject))]
public class CommandSubjectPropertyDrawer : PropertyDrawer 
{
    static float lineHeight = 16;
    public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    {

        PropertyIndexer iProperty = new PropertyIndexer(property);
        SubjectiveScrollList scrollList = property.GetComponentInParent<SubjectiveScrollList>();
        
        SetContentFromScrollList(scrollList, iProperty);
        UnityEngine.Object preContent = iProperty["content"].objectReferenceValue;

        bool hasContent = iProperty["content"].objectReferenceValue || scrollList;
        Rect pos = new Rect(position);  pos.height = lineHeight;
        
        //Content
        EditorGUI.PropertyField(pos, iProperty["content"], label);
        
        //Reset Method Path
        bool contentChanged = (preContent != iProperty["content"].objectReferenceValue) && (iProperty["content"].objectReferenceValue != null);
        if (contentChanged)   ResetSubject(iProperty);

        if (hasContent)
        {
            List<string> links = GetMethodLinks(iProperty);
            int index = links.IndexOf(iProperty["methodPath"].stringValue);
            int preIndex = index;
            int indent = EditorGUI.indentLevel;
            EditorGUI.indentLevel = 1;
            
            pos.y += lineHeight;
            index = EditorGUI.Popup(pos, "Method Path", index, links.ToArray());
            if (index > -1) iProperty["methodPath"].stringValue = links[index] == "NONE" ? string.Empty : links[index];

            if (index != preIndex)
                UpdateParameters(iProperty);
            InspectParametersGUI(iProperty, pos);
            EditorGUI.indentLevel = indent;
        }
    }

    private void  InspectParametersGUI(PropertyIndexer iProperty, Rect pos)
    {
        string sourceLabel = string.Empty;
        SubjectiveScrollList scrollList = iProperty.property.GetComponentInParent<SubjectiveScrollList>();

        //Parameters
        iProperty["m_parameters"].ForEachItem("", p =>
        {
            PropertyIndexer ip = new PropertyIndexer(p);
            sourceLabel = ip["name"].stringValue + "(" + ip["type"].stringValue + ")";
            pos.y += lineHeight;
            EditorGUI.PropertyField(pos, ip["source"], new GUIContent(sourceLabel));
            if (ip["source"].objectReferenceValue || scrollList)
            {
                List<string> mlinks = GetMemberLinks(ip);

                int mindex = mlinks.IndexOf(ip["memberPath"].stringValue);

                pos.y += lineHeight;
                mindex = EditorGUI.Popup(pos, "Member Path", mindex, mlinks.ToArray());
                if (mindex > -1)
                    ip["memberPath"].stringValue = mlinks[mindex] == "NONE" ? string.Empty : mlinks[mindex];
            }
            return false;
        });
    }

    private static void ResetSubject(PropertyIndexer iProperty)
    {
        iProperty["methodPath"].stringValue = string.Empty;
        iProperty["m_parameters"].ClearArray();
    }

    void UpdateParameters(PropertyIndexer s)
    {
        s["m_parameters"].ClearArray();
        if (s["methodPath"].stringValue != string.Empty)
        {
            MethodInfo methodInfo = null;
            if (s["content"].objectReferenceValue)
            {
                methodInfo = s["content"].objectReferenceValue.GetMethodInfo(s["methodPath"].stringValue);

                ParameterInfo[] param = methodInfo.GetParameters();
                for (int i = 0; i < param.Length; i++)
                {
                    s["m_parameters"].InsertArrayElementAtIndex(i);
                    PropertyIndexer p = new PropertyIndexer(s["m_parameters"].GetArrayElementAtIndex(i));
                    p["type"].stringValue = param[i].ParameterType.ToString();
                    p["name"].stringValue = param[i].Name;
                }
            }
        }
    }

    private void SetContentFromScrollList(SubjectiveScrollList scrollList, PropertyIndexer iProperty)
    {
        if(scrollList.isNotNone())
        {
            if (iProperty["content"].objectReferenceValue.isNone())
                iProperty["content"].objectReferenceValue = scrollList.subject.content;
        }
    }
    
    List<string> GetMethodLinks(PropertyIndexer property)
    {
        List<string> mlinks = new List<string>();

        if (property["content"].objectReferenceValue.isNotNone() )
            mlinks = property["content"].objectReferenceValue.MethodsPath();
        
        mlinks.Insert(0, "NONE");
        return mlinks;
    }

    List<string> GetMemberLinks(PropertyIndexer property)
    {
        Type type = TypeExtension.GetType(property["type"].stringValue);
        List<string> mlinks = new List<string>();
        SubjectiveScrollList scrollList = property.property.GetComponentInParent<SubjectiveScrollList>();

        if (property["source"].objectReferenceValue.isNotNone())
        {
            mlinks = property["source"].objectReferenceValue.MemberPathsInclusiveTypes(
           false, "", new Type[]{type});
        }
        else if (scrollList)
        {
            if (scrollList.subject.hasContent)
            {
                Type valueType = scrollList.subject.value.GetType();
                string typeName = valueType.ToString().GetStringBetween("[", "]");
                valueType = TypeExtension.GetType(typeName);
                mlinks = valueType.MemberPathsFromType(false,
                    "", new Type[] { type });
            }
        }
        mlinks.Insert(0, "NONE");
        return mlinks;
    }


    public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
    {
        PropertyIndexer iProperty = new PropertyIndexer(property);
        SubjectiveScrollList scrollList = property.GetComponentInParent<SubjectiveScrollList>();
        bool hasContent = iProperty["content"].objectReferenceValue;
        int paramCount = 0;
        if(hasContent)
            iProperty["m_parameters"].ForEachItem("", p =>
            {
                PropertyIndexer ip = new PropertyIndexer(p);
                paramCount += (ip["source"].objectReferenceValue || scrollList ? 2 : 1);
                return false;
            });
        return hasContent ? (2 + paramCount) * lineHeight : lineHeight;
    }
}
